package dbserver;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.concurrent.LinkedBlockingQueue;

public class Listener implements Runnable{
	private ArrayList<Client> clients;
	private Client currentClient;
	private TcpIPServer owner;
	private QueueManager manager;
	private int ID;
	private Model model;
	
	public Listener(TcpIPServer owner, Socket clientSoc, int number, QueueManager manager){
		this.clients = new ArrayList<Client>();
		this.ID = number;
		int clientID = Integer.parseInt("" + number + owner.getNumberOfClients());
		Client client = new Client(clientSoc, clientID);
		clients.add(client);
		this.owner = owner;
		this.manager = manager;
		model = null;
	}
	public Listener(TcpIPServer owner, Socket clientSoc, int number, Model model){
		this.clients = new ArrayList<Client>();
		this.ID = number;
		int clientID = Integer.parseInt("" + number + owner.getNumberOfClients());
		Client client = new Client(clientSoc, clientID);
		clients.add(client);
		this.owner = owner;
		this.manager = null;
		this.model = model;
	}
	@Override
	public void run() {
		while(clients.size() > 0){
			Client clientToRemove = null;
			synchronized (clients){
				for(Client client: clients){
					Socket clientSocket = client.getSocket();
					BufferedReader br = null;
					PrintWriter pw = null;
					int available = 0;
					try {
						available = clientSocket.getInputStream().available();
					} catch (IOException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
					if (available > 0) {
						try{
							br = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
							String queryLine = br.readLine();
							if(manager != null){
								Query query = new Query(client.getID(), queryLine);
								manager.putQuery(query);
							}
							else
								if (model != null){
									model.findText(queryLine);
									Record rec = model.getCurrentState();
									String result = "" + rec.getId() + "#" +
													rec.getTerm() + "#" +
													rec.getValue();
									send(result, client.getID());
								}
						}
						catch (IOException e){
							e.printStackTrace();
						}
					} else {
						try{
							pw = new PrintWriter(clientSocket.getOutputStream(), true);
							pw.println("ping");
							if(pw.checkError()){
								clientToRemove = client;
							}
						}
						catch (IOException e){
							clientToRemove = client;
						}
					}
				}
			}
			if(clientToRemove != null){
				clients.remove(clientToRemove);
				owner.decreaseNumOfClients();
			}
		}
		owner.removeListener(this);
	}
	public void addClient(Socket clientSoc){
			synchronized (clients){
				int clientID = Integer.parseInt("" + this.ID + owner.getNumberOfClients());
				Client client = new Client(clientSoc, clientID);
				clients.add(client);
			}
	}
	public int getNumberOfClients(){
		return clients.size();
	}
	public ArrayList<Client> getClients(){
		return clients;
	}
	public void removeClient(Socket client){
		synchronized (clients){
			try {
				client.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			clients.remove(client);
			owner.decreaseNumOfClients();
		}
	}
	public void send(Object o, int clientID){
		synchronized (clients){
			for(Client client: clients){
				if(client.getID() == clientID){
					Socket clientSoc = client.getSocket();
					try{
						ObjectOutputStream oos = new ObjectOutputStream(clientSoc.getOutputStream());
						oos.writeObject(o);
						oos.flush();
					}
					catch (IOException e){
						e.printStackTrace();
					}
				}
			}
		}
	}
	public void send(String str, int clientID){
		synchronized (clients){
			for(Client client: clients){
				if(client.getID() == clientID){
					Socket clientSoc = client.getSocket();
					try{
						PrintWriter pw = new PrintWriter(clientSoc.getOutputStream(), true);
						pw.println(str);
					}
					catch (IOException e){
						e.printStackTrace();
					}
				}
			}
		}
	}
}
